/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package naivebayes;

import id3.Attribute;
import id3.AttributeDetail;
import id3.InputFile;
import id3.Record;
import id3.Utility;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 *
 * @author Ifun
 */
public class NaiveBayes {
    private Map<String, Map<String,Integer>> frequencyTable;
    private Map<String, Integer> frequencyClass;
    private Map<String, Double> probabilityClass;
    private Map<String, Map<String, Double>> probabilityTable;
//    private Map<String, ArrayList<String>> attributesKey;
    private String className;
    private int nData;
    
    public NaiveBayes(ArrayList<Record> inputTraining, ArrayList<AttributeDetail> attributeDesc)
    {
        frequencyTable = new LinkedHashMap<String, Map<String, Integer>>();
        frequencyClass = new LinkedHashMap<String, Integer>();
        probabilityClass = new LinkedHashMap<String, Double>();
        probabilityTable = new LinkedHashMap<String, Map<String, Double>>();
        
        initializeData(inputTraining, attributeDesc);
        generateProbability(attributeDesc);
    }
    
    private void initializeData(ArrayList<Record> training, ArrayList<AttributeDetail> attributeDesc)
    {
        AttributeDetail classDetail = attributeDesc.get(attributeDesc.size()-1);
        className = classDetail.nama;
        
        nData = training.size();
//        ArrayList<Attribute> row = training.get(0).attributes;
        for(int i=0;i<classDetail.list_value.size();++i)
        {
            frequencyClass.put(classDetail.list_value.get(i), 0);
        }
        for(int i=0; i<attributeDesc.size()-1;++i)
        {
            Map<String, Integer> attributeValCount = new LinkedHashMap<String, Integer>();
            ArrayList<String> listVal = attributeDesc.get(i).list_value;
            for(int j=0;j<listVal.size();++j)
            {
                for(Map.Entry<String, Integer> cEntry: frequencyClass.entrySet())
                {
                    String key = listVal.get(j).concat(',' + cEntry.getKey());
                    attributeValCount.put(key, 1);
                    frequencyTable.put(attributeDesc.get(i).nama, attributeValCount);
                }
            }
        }
        
        for(Record record:training)
        {
            int recordLength = record.attributes.size();
            for(int i=0;i<recordLength-1;++i)
            {
                String attribName = record.attributes.get(i).name;
                Map<String, Integer> attributeVal = frequencyTable.get(attribName);
                String key = record.attributes.get(i).value.concat(',' + record.attributes.get(recordLength-1).value);
//                if(attributeVal.containsKey(key))
//                {
                    int num = attributeVal.get(key);
                    attributeVal.put(key, ++num);
//                }
//                else
//                {
//                    attributeVal.put(key, 1);
//                }
//                ArrayList<String> listVal = attributesKey.get(attribName);
//                if(!listVal.contains(record.attributes.get(i).value))
//                {
//                    listVal.add(record.attributes.get(i).value);
//                    attributesKey.put(attribName, listVal);
//                }
                frequencyTable.put(attribName,attributeVal);
            }
            String key = record.attributes.get(recordLength-1).value;
//            if(frequencyClass.containsKey(key))
//            {
                int num = frequencyClass.get(key);
                frequencyClass.put(key, ++num);
//            }
//            else
//            {
//                frequencyClass.put(key, 1);
//            }
        }
    }
    
    private void generateProbability(ArrayList<AttributeDetail> attributeDesc)
    {
        for(Map.Entry<String, Map<String,Integer>> freqEntry: frequencyTable.entrySet())
        {
            Map<String, Double> probAtrib = new LinkedHashMap<String, Double>();
            Map<String, Integer> freqAtrib = freqEntry.getValue();
            int numAttribVal = freqAtrib.size() / frequencyClass.size();
            for(Map.Entry<String, Integer> freqAtribEntry: freqAtrib.entrySet())
            {
                String key = freqAtribEntry.getKey();
                String classAtrib = key.substring(key.indexOf(',')+1);
                double prob = ((double)freqAtribEntry.getValue()/(double) (frequencyClass.get(classAtrib) + numAttribVal));
                probAtrib.put(key, prob);
            }
            probabilityTable.put(freqEntry.getKey(), probAtrib);
        }
        
        for(Map.Entry<String, Integer> classEntry: frequencyClass.entrySet())
        {
            double prob = ((double) classEntry.getValue()/(double)nData);
            probabilityClass.put(classEntry.getKey(), prob);
        }
    }
    
    public String classify(Record rTes)
    {
        String result = "";
//        ArrayList<String> listClass = attributesKey.get(className);
        double max = 0;
        for(Map.Entry<String, Double> probClassEntry:probabilityClass.entrySet())
        {
            double probability = 1;
            for(int i=0;i<rTes.attributes.size()-1;++i)
            {
                Map<String, Double> probAtrib = probabilityTable.get(rTes.attributes.get(i).name);
                String key = rTes.attributes.get(i).value.concat("," + probClassEntry.getKey());
//                if(probAtrib.get(key)!=null)
//                {
                    probability *= ((double)probAtrib.get(key));
//                }
//                else
//                {
//                    probability *=0;
//                }
            }
            probability *= probClassEntry.getValue();
            if(max<probability)
            {
                max = probability;
                result = probClassEntry.getKey();
            }
        }
        
        return result;
    }
    
    public ArrayList<String> inference(ArrayList<Record> tes)
    {
        ArrayList<String> result = new ArrayList<String>();
        for(int i=0;i<tes.size();++i)
        {
            result.add(classify(tes.get(i)));
        }
        
        return result;
    }

    public void printFrequency()
    {
        for(Map.Entry<String, Map<String,Integer>> fEntry: frequencyTable.entrySet())
        {
            int n = fEntry.getValue().size()/frequencyClass.size();
            System.out.println("- Attribute : " + fEntry.getKey() + " , " + n);
            for(Map.Entry<String,Integer> vEntry: fEntry.getValue().entrySet())
            {
                System.out.println("--- " + vEntry.getKey() + " : " + vEntry.getValue());
            }
        }
        System.out.println("Class : " + className);
        for(Map.Entry<String, Integer> cEntry: frequencyClass.entrySet())
        {
            System.out.println("Attribute Class : " + cEntry.getKey() + " : " + cEntry.getValue());
        }
    }
    
    public void printProbability()
    {
        for(Map.Entry<String, Map<String,Double>> pEntry: probabilityTable.entrySet())
        {
            System.out.println("- Attribute : " + pEntry.getKey());
            for(Map.Entry<String,Double> vEntry: pEntry.getValue().entrySet())
            {
                System.out.println("--- " + vEntry.getKey() + " : " + vEntry.getValue());
            }
        }
        System.out.println("Class : " + className);
        for(Map.Entry<String, Double> cEntry: probabilityClass.entrySet())
        {
            System.out.println("Attribute Class : " + cEntry.getKey() + " : " + cEntry.getValue());
        }
    }
    
//    public void printAttributes()
//    {
//        for(Map.Entry<String, ArrayList<String>> atEntry: attributesKey.entrySet())
//        {
//            System.out.println("Attribut : " + atEntry.getKey());
//            System.out.print("Value : ");
//            for(String val: atEntry.getValue())
//            {
//                System.out.print(val + ", ");
//            }
//            System.out.println("");
//        }
//    }
    
    public static void main(String[] args) throws IOException{
        ArrayList<Record> records;
        ArrayList<Record> recordsTes;
        records = InputFile.buildRecords("D:/Kuliah/Tugas/Semester 7/Machine Learning/tubes source code/v1/data/krkopt.data.txt");
        //recordsTes = InputFile.buildRecordTest("C:/Users/Lenovo/Documents/NetBeansProjects/Topsus/data/car.data-test.txt");
        
        NaiveBayes nb = new NaiveBayes(records, Utility.attributeDetails);
////        nb.printAttributes();
        System.out.println("----------------");
        nb.printFrequency();
        System.out.println("----------------");
        nb.printProbability();
        System.out.println("----------------");
//        recordsTes.get(0).PrintRecord();
//        System.out.println(nb.classify(recordsTes.get(0)));
//        System.out.println("----------------");
//        ArrayList tesResult = nb.inference(recordsTes);
//        for(int i=0;i<tesResult.size();++i)
//        {
//            System.out.println(tesResult.get(i));
//        }
    }
}
